/*
 * @Description:
 * @User: Vijay <1937832819@qq.com>
 * @Date: 2020-10-20 22:34:11
 */
package commons

import (
	"strings"
	"sync"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/context"
	"github.com/dgrijalva/jwt-go"
	"github.com/go-redis/redis/v7"
)

// Helper 助手类
type Helper struct {
}

/**
 * @Description: token 验证
 * @User: Vijay <1937832819@qq.com>
 * @Date: 2020-11-06 00:10:37
 * @param {*}
 * @return {*}
 */
func (h *Helper) ParseToken(ctx *context.Context) (t *jwt.Token, e interface{}) {
	authString := ctx.Input.Header("Authorization")
	// beego.Debug("AuthString:", authString)
	kv := strings.Split(authString, " ")
	if len(kv) != 2 || kv[0] != "Bearer" {
		beego.Error("AuthString invalid:", authString)
		return nil, "AuthString无效"
	}
	tokenString := kv[1]
	// beego.Debug("Token:", tokenString)

	// token是否无效
	cache, err := h.NewRedisClient()
	if err != nil {
		return nil, "缓存redis开启失败"
	}
	// 获取黑名单，在黑名单中就是已过期的token
	tokenExpired, err := cache.Get(tokenString).Result()
	if tokenExpired != "" {
		return nil, "token已过期"
	}
	// Parse token
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		return []byte("mykey"), nil
	})
	if err != nil {
		beego.Error("Parse token:", err)
		if ve, ok := err.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
				// That's not even a token
				return nil, err
			} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
				// Token is either expired or not active yet
				return nil, "令牌已过期或尚未激活"
			} else {
				// Couldn't handle this token
				return nil, "无法处理此令牌"
			}
		} else {
			// Couldn't handle this token
			return nil, "无法处理此令牌"
		}
	}
	if !token.Valid {
		beego.Error("Token invalid:", tokenString)
		return nil, "令牌无效"
	}
	return token, nil
}

var (
	once        sync.Once
	redisClient *redis.Client
)

// NewRedisClient 设置缓存
func (h *Helper) NewRedisClient() (*redis.Client, error) {
	once.Do(func() {
		index, _ := beego.AppConfig.Int("redis::index")
		redisClient = redis.NewClient(&redis.Options{
			Addr:     beego.AppConfig.String("redis::addr"),
			Password: beego.AppConfig.String("redis::password"),
			DB:       index,
			PoolSize: 10,
		})
	})

	_, err := redisClient.Ping().Result()
	if err != nil {
		return nil, err
	} else {
		return redisClient, nil
	}
}
